#include "GeminiGUI.h"
#include <string>

////////////////////////////////////////////////////////
// GeminiGUIApp Code Begins

IMPLEMENT_APP (GeminiGUIApp);

bool GeminiGUIApp::OnInit ()
{
  GeminiGUIFrame *frame = new GeminiGUIFrame ("Gemini Simulator");
  SetTopWindow (frame);
  frame->Show (TRUE);
  return true;
}

// GeminiGUIApp Code Ends
////////////////////////////////////////////////////////

////////////////////////////////////////////////////////
// GeminiGUIFrame Code Begins

GeminiGUIFrame::GeminiGUIFrame (const char *szAppName) :
  wxFrame (NULL, -1, (wxChar*)szAppName, wxDefaultPosition, wxDefaultSize, 
      wxDEFAULT_FRAME_STYLE & (~(wxRESIZE_BORDER | wxMAXIMIZE_BOX)))
{
  wxMenuBar* menubar = CreateMenuBar ();
  SetMenuBar (menubar);

  wxPanel *panel = new wxPanel (this, -1, wxDefaultPosition, 
      wxDefaultSize, wxNO_FULL_REPAINT_ON_RESIZE);
  wxBoxSizer *vbox = new wxBoxSizer( wxVERTICAL );
  wxBoxSizer *hbox = new wxBoxSizer( wxHORIZONTAL );
  wxBoxSizer *bottombox = new wxBoxSizer( wxHORIZONTAL );
  wxFlexGridSizer *gridsizer = new wxFlexGridSizer( 2, 0, 0 );
  wxFlexGridSizer *bottomsizer = new wxFlexGridSizer( 2, 0, 0 );
  wxBoxSizer *buttonsizer = new wxBoxSizer( wxVERTICAL );

  listbox = new wxListBox( panel, -1, wxDefaultPosition, wxSize(200,100), 0, NULL, wxLB_SINGLE );
  //listbox->Enable( false );
  hbox->Add( listbox, 0, wxGROW|wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );

  wxStaticText *acclabel = new wxStaticText( panel, -1, wxT("ACC"));
  wxStaticText *alabel = new wxStaticText( panel, -1, wxT("A"));
  wxStaticText *blabel = new wxStaticText( panel, -1, wxT("B"));
  wxStaticText *templabel = new wxStaticText( panel, -1, wxT("TEMP"));
  wxStaticText *marlabel = new wxStaticText( panel, -1, wxT("MAR"));
  wxStaticText *mdrlabel = new wxStaticText( panel, -1, wxT("MDR"));
  wxStaticText *pclabel = new wxStaticText( panel, -1, wxT("PC"));
  wxStaticText *irlabel = new wxStaticText( panel, -1, wxT("IR"));
  wxStaticText *cclabel = new wxStaticText( panel, -1, wxT("CC"));

  acctext = new wxTextCtrl( panel, -1, wxT(""), wxDefaultPosition, wxSize(80,-1), wxTE_READONLY );
  atext = new wxTextCtrl( panel, -1, wxT(""), wxDefaultPosition, wxSize(80,-1), wxTE_READONLY );
  btext = new wxTextCtrl( panel, -1, wxT(""), wxDefaultPosition, wxSize(80,-1), wxTE_READONLY );
  temptext = new wxTextCtrl( panel, -1, wxT(""), wxDefaultPosition, wxSize(80,-1), wxTE_READONLY );
  martext = new wxTextCtrl( panel, -1, wxT(""), wxDefaultPosition, wxSize(80,-1), wxTE_READONLY );
  mdrtext = new wxTextCtrl( panel, -1, wxT(""), wxDefaultPosition, wxSize(80,-1), wxTE_READONLY );
  pctext = new wxTextCtrl( panel, -1, wxT(""), wxDefaultPosition, wxSize(80,-1), wxTE_READONLY );
  irtext = new wxTextCtrl( panel, -1, wxT(""), wxDefaultPosition, wxSize(80,-1), wxTE_READONLY );
  cctext = new wxTextCtrl( panel, -1, wxT(""), wxDefaultPosition, wxSize(80,-1), wxTE_READONLY );

  gridsizer->Add( acclabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  gridsizer->Add( acctext, 0, wxALIGN_CENTER|wxALL, 5 );
  gridsizer->Add( alabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  gridsizer->Add( atext, 0, wxALIGN_CENTER|wxALL, 5 );
  gridsizer->Add( blabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  gridsizer->Add( btext, 0, wxALIGN_CENTER|wxALL, 5 );
  gridsizer->Add( templabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  gridsizer->Add( temptext, 0, wxALIGN_CENTER|wxALL, 5 );
  gridsizer->Add( marlabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  gridsizer->Add( martext, 0, wxALIGN_CENTER|wxALL, 5 );
  gridsizer->Add( mdrlabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  gridsizer->Add( mdrtext, 0, wxALIGN_CENTER|wxALL, 5 );
  gridsizer->Add( pclabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  gridsizer->Add( pctext, 0, wxALIGN_CENTER|wxALL, 5 );
  gridsizer->Add( irlabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  gridsizer->Add( irtext, 0, wxALIGN_CENTER|wxALL, 5 );
  gridsizer->Add( cclabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  gridsizer->Add( cctext, 0, wxALIGN_CENTER|wxALL, 5 );

  hbox->Add( gridsizer, 0, wxALIGN_CENTER|wxALL, 5 );
  vbox->Add( hbox, 0, wxALIGN_CENTER|wxALL, 5 );

  wxStaticText 	*binlabel, *misslabel, *hitlabel;
  binlabel = new wxStaticText( panel, -1, wxT("Binary Instruction"));
  hitlabel = new wxStaticText( panel, -1, wxT("Hits Count"));
  misslabel = new wxStaticText( panel, -1, wxT("Misses Count"));

  bintext = new wxStaticText( panel, -1, wxT(""), wxDefaultPosition, wxSize(140,-1), wxALIGN_RIGHT );
  hittext = new wxStaticText( panel, -1, wxT(""), wxDefaultPosition, wxSize(140,-1), wxALIGN_RIGHT );
  misstext = new wxStaticText( panel, -1, wxT(""), wxDefaultPosition, wxSize(140,-1), wxALIGN_RIGHT );

  bottomsizer->Add( binlabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  bottomsizer->Add( bintext, 0, wxALIGN_CENTER|wxALL, 5 );
  bottomsizer->Add( 20, 2, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  bottomsizer->Add( 20, 2, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  bottomsizer->Add( hitlabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  bottomsizer->Add( hittext, 0, wxALIGN_CENTER|wxALL, 5 );
  bottomsizer->Add( 20, 2, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  bottomsizer->Add( 20, 2, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  bottomsizer->Add( misslabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  bottomsizer->Add( misstext, 0, wxALIGN_CENTER|wxALL, 5 );

  bottombox->Add( bottomsizer, 0, wxGROW|wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );

  nextbutton = new wxButton( panel, ID_NEXT_BUTTON, wxT("Next"));
  stepbutton = new wxButton( panel, ID_STEP_BUTTON, wxT("Step"));
  contbutton = new wxButton( panel, ID_CONT_BUTTON, wxT("Continue"));

  buttonsizer->Add( nextbutton, 0, wxALIGN_CENTER|wxALL, 5 );
  buttonsizer->Add( stepbutton, 0, wxALIGN_CENTER|wxALL, 5 );
  buttonsizer->Add( contbutton, 0, wxALIGN_CENTER|wxALL, 5 );

  bottombox->Add( buttonsizer, 0, wxGROW|wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );

  vbox->Add( bottombox, 0, wxALIGN_CENTER|wxALL, 5 );

  panel->SetSizer( vbox );
  vbox->SetSizeHints( panel );
  vbox->SetSizeHints( this );

  cpu = new CPU (1);
}

GeminiGUIFrame::~GeminiGUIFrame()
{
  if (cpu) delete cpu;
  cpu = NULL;
}

void GeminiGUIFrame::ExecutionComplete ()
{
  nextbutton->Enable (false);
  stepbutton->Enable (false);
  contbutton->Enable (false);
}

void GeminiGUIFrame::UpdateGUI ()
{
  acctext->SetValue (wxString::Format (wxT("0x%08x"), cpu->ACC));
  atext->SetValue (wxString::Format (wxT("0x%08x"), cpu->A));
  btext->SetValue (wxString::Format (wxT("0x%08x"), cpu->B));
  temptext->SetValue (wxString::Format (wxT("0x%08x"), cpu->TEMP));
  martext->SetValue (wxString::Format (wxT("0x%08x"), cpu->MAR));
  mdrtext->SetValue (wxString::Format (wxT("0x%08x"), cpu->MDR));
  pctext->SetValue (wxString::Format (wxT("0x%08x"), cpu->PC));
  irtext->SetValue (wxString::Format (wxT("0x%08x"), cpu->IR));
  cctext->SetValue (wxString::Format (wxT("0x%08x"), cpu->CC));

  hittext->SetLabel (wxString::Format (wxT("%d"), *(cpu->mem.nhit)));
  misstext->SetLabel (wxString::Format (wxT("%d"), *(cpu->mem.nmiss)));
  bintext->SetLabel (wxString::FromAscii(cpu->binaryOperation().data ()));

  if (cpu->IR != -1 && cpu->IR < static_cast<int>(cpu->asm_instrs.size()))
    listbox->SetSelection (cpu->IR);
}

wxMenuBar* GeminiGUIFrame::CreateMenuBar ()
{
  wxMenuBar *menubar = new wxMenuBar ();
  wxMenu *filemenu = new wxMenu ();

  filemenu->Append (ID_FILE_EXECUTE, wxT("&Execute"));
  filemenu->Append (ID_FILE_BUILD_EXECUTE, wxT("&Build And Execute"));
  filemenu->Append (ID_FILE_EXIT, wxT("E&xit"));

  menubar->Append (filemenu, wxT("&File"));
  return menubar;
}

void GeminiGUIFrame::OnFileExecute (wxCommandEvent& ce)
{
  wxFileDialog *fd = new wxFileDialog (this);
  char* fname;

  if (fd->ShowModal () == wxID_OK)
  {
    const char* t_fname = fd->GetPath ().mb_str(wxConvUTF8);
    fname = const_cast<char*> (t_fname);
    cpu->Load (fname);
    for (unsigned int i = 0; i < cpu->asm_instrs.size (); i++)
    {
      //const char* t_instr = mb_str(wxConvUTF8);
      listbox->Append (wxString::FromAscii(cpu->asm_instrs[i].c_str()));
    }
  }
}

void GeminiGUIFrame::OnFileBuildAndExecute (wxCommandEvent& ce)
{
  wxFileDialog *fd = new wxFileDialog (this);
  char* fname;

  if (fd->ShowModal () == wxID_OK)
  {
    const char* t_fname = fd->GetPath ().mb_str(wxConvUTF8);
    fname = const_cast<char*> (t_fname);
    Assembler (fname, "out.log");
    cpu->Load ("out.log");
    for (unsigned int i = 0; i < cpu->asm_instrs.size (); i++)
    {
      listbox->Append (wxString::FromAscii(cpu->asm_instrs[i].c_str()));
    }
  }
}

void GeminiGUIFrame::OnFileExit (wxCommandEvent& ce)
{
  Close ();
}

void GeminiGUIFrame::OnNextButton (wxCommandEvent& ce)
{
  if (cpu->opNum == 1)
    cpu->Decode ();
  else if (cpu->opNum < cpu->opTotal)
    cpu->ExecuteNextCycle ();
  else if (cpu->Fetch () == -1)
    ExecutionComplete ();
  UpdateGUI ();
}

void GeminiGUIFrame::OnStepButton (wxCommandEvent& ce)
{
  if (cpu->opNum >= cpu->opTotal) {
    if (cpu->Fetch () == -1)
      ExecutionComplete ();
  }
  else if (cpu->opNum == 1)
    cpu->Decode ();
  else
    cpu->ExecuteNextInstruction ();

  UpdateGUI ();
}

void GeminiGUIFrame::OnContinueButton (wxCommandEvent& ce)
{
  while (1)
  {
    if (cpu->opNum >= cpu->opTotal && cpu->Fetch () == -1) {
      ExecutionComplete ();
      break;
    }
    else if (cpu->opNum == 1)
      cpu->Decode ();
    else
      cpu->ExecuteNextInstruction ();
  }

  UpdateGUI ();
}

  BEGIN_EVENT_TABLE(GeminiGUIFrame,wxFrame)
  EVT_MENU (ID_FILE_EXECUTE, GeminiGUIFrame::OnFileExecute)
  EVT_MENU (ID_FILE_BUILD_EXECUTE, GeminiGUIFrame::OnFileBuildAndExecute)
EVT_MENU (ID_FILE_EXIT, GeminiGUIFrame::OnFileExit)

  EVT_BUTTON (ID_NEXT_BUTTON,GeminiGUIFrame::OnNextButton)
  EVT_BUTTON (ID_STEP_BUTTON,GeminiGUIFrame::OnStepButton)
  EVT_BUTTON (ID_CONT_BUTTON,GeminiGUIFrame::OnContinueButton)
END_EVENT_TABLE()

  // GeminiGUIFrame Code Ends
  ////////////////////////////////////////////////////////

void errprintline (const char *msg, int line)
{
#ifdef __ABCD__
  wxMessageDialog *md = new wxMessageDialog (NULL, msg , wxT("Error"), wxOK);
  md->ShowModal ();
  exit (1);
#endif
}
